//@author A0105546J
package tasky.history;

import java.util.ArrayList;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

import tasky.commons.Task;
import static org.junit.Assert.assertTrue;

public class History {

	public Stack<HistoryStatus> undoStack;
	private Stack<HistoryStatus> redoStack;
	private HistoryStatus undoStatus;
	private HistoryStatus undoStatusClone;
	private HistoryStatus redoStatus;
	private HistoryStatus redoStatusClone;
	private static Logger logger = Logger.getLogger("History");

	private static final String NO_HISTORY = "No operation has been performed.\n";
	private static final String HISTORY_HEADER = "----- History -----     (the latest operation on top)\n";
	private static final String HISTORY_END = "----- End of history -----\n";

	public History() {

		undoStack = new Stack<HistoryStatus>();
		redoStack = new Stack<HistoryStatus>();

	}

	public void storeUndo(ArrayList<Task> list, String command) {

		logger.log(Level.INFO, "starting storeUndo");

		try {
			undoStack.push(cloneHistoryStatus(list, command));
			redoStack = new Stack<HistoryStatus>();
		} catch (Exception e) {
			logger.log(Level.WARNING, "storeUndo error", e);
		}

		logger.log(Level.INFO, "end of storeUndo");

	}

	// This method is used to create a new copy of the list, so that there will
	// be one unique list of tasks for each HistoryStatus.
	private HistoryStatus cloneHistoryStatus(ArrayList<Task> list,
			String command) {

		return CloneHistoryStatus.execute(list, command);

	}

	public HistoryStatus undo(ArrayList<Task> list) {

		if (undoStack.size() != 0) {

			logger.log(Level.INFO, "starting undo");

			try {
				executeUndo(list);
			} catch (Exception e) {
				logger.log(Level.WARNING, "undo error", e);
			}

			logger.log(Level.INFO, "end of undo");

		} else {

			return null;

		}

		return undoStatus;

	}

	private void executeUndo(ArrayList<Task> list) {

		undoStatus = undoStack.pop();
		undoStatusClone = cloneHistoryStatus(list, undoStatus.getCommand());

		assertTrue(undoStatusClone != null);

		redoStack.push(undoStatusClone);

	}

	public HistoryStatus redo(ArrayList<Task> list) {

		if (redoStack.size() != 0) {

			logger.log(Level.INFO, "starting redo");

			try {
				executeRedo(list);
			} catch (Exception e) {
				logger.log(Level.WARNING, "redo error", e);
			}

			logger.log(Level.INFO, "end of redo");

		} else {

			return null;

		}

		return redoStatus;

	}

	private void executeRedo(ArrayList<Task> list) {

		redoStatus = redoStack.pop();
		redoStatusClone = cloneHistoryStatus(list, redoStatus.getCommand());

		assertTrue(redoStatusClone != null);

		undoStack.push(redoStatusClone);
	}

	public String showHistory() {

		String historyList = "";

		if (undoStack.size() == 0) {
			historyList = NO_HISTORY;
		} else {

			logger.log(Level.INFO, "starting constructing HistoryList");

			try {
				historyList = constructHistoryList(historyList);
			} catch (Exception e) {
				logger.log(Level.WARNING, "construction error", e);
			}

			logger.log(Level.INFO, "end of constructing HistoryList");

		}

		return historyList;

	}

	// This method is used to build the output String.
	private String constructHistoryList(String historyList) {

		historyList = historyList + HISTORY_HEADER;

		for (int i = undoStack.size() - 1; i >= 0; i--) {

			historyList = historyList + (undoStack.size() - i) + ". "
					+ undoStack.get(i).getCommand() + "\n";

		}

		historyList = historyList + HISTORY_END;

		return historyList;
	}

}
